﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using NetCore.Cache;
using Autofac;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.FileProviders;
using NetCore.BLL;
using Microsoft.Extensions.Caching.Distributed;
using log4net;
using log4net.Config;
using System.Text.RegularExpressions;
using NetCore.ORM.EF;
using NetCore.ORM.Ado;
using NetCore.ORM;

using System.Runtime.Loader;

namespace NetCore.Web
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        
        public IConfiguration Configuration { get; }
        public Autofac.IContainer ApplicationContainer;

        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => false;
                options.MinimumSameSitePolicy = SameSiteMode.None;
               
            });
        
            //负载均衡中间件
            services.Configure<ForwardedHeadersOptions>(options =>
            {
                options.ForwardedHeaders =
                    ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
            });
        
            services.AddResponseCompression();//压缩文件
            services.AddResponseCaching();
            //系统配置
            ApplicationEnvironments.Site = Configuration.GetSection("SiteConfig").Get<SiteConfig>();
            ApplicationEnvironments.ProviderType = (ProviderType)Enum.Parse(typeof(ProviderType), ApplicationEnvironments.Site.ProviderType);

            ///log4日志记录
            var logRepository = LogManager.CreateRepository("NETCORERepository");

            XmlConfigurator.Configure(logRepository, new FileInfo(AppDomain.CurrentDomain.BaseDirectory + "/log4net.config"));
            LogHelper.Init( logRepository);
            //httpcontext 如果在dll层需要用到httpContext则需要使用方法
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        
            // services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));
            //属性注入
            services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
            var mvcBuilders = services.AddMvc(config => {
               
                 config.Filters.Add<CustomExceptionFilter>();
                //config.ModelValidatorProviders.Clear();
                //config.MaxModelValidationErrors = 0;
                config.ModelBinderProviders.Insert(0, new CustomModelBinderProvider());
                config.CacheProfiles.Add("default", new Microsoft.AspNetCore.Mvc.CacheProfile
                {
                    Duration = 600 /*10分钟*/
                });
                // config.ModelValidatorProviders.Insert(0, new CustomModelValidatorProvider());
                //  config.ModelBinderProviders.Insert(0, new CustomModelBinderProvider());

            }
            )
            
         
            //全局配置Json序列化处理
        .AddJsonOptions(options =>
        {
            //忽略循环引用
            options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            //不使用驼峰样式的key
            options.SerializerSettings.ContractResolver = new DefaultContractResolver();
            ////设置时间格式
            //options.SerializerSettings.DateFormatString = "yyyy-MM-dd";
        }
        )
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
                .AddSessionStateTempDataProvider();

            //去除自带mvc验证。
           // services.Configure<ApiBehaviorOptions>(options=>options.SuppressModelStateInvalidFilter=false);
            #region  //身份认证时需要使用的方法
            ICacheService cacheService = null;
            //ICacheService cacheSessionService = null;
            //缓存注入
            if (ApplicationEnvironments.Site.IsUseRedis)
            {
                cacheService = new RedisService(ApplicationEnvironments.Site.Redis.DataBase);
            //    cacheSessionService = new RedisService(ApplicationEnvironments.Site.Redis.SessionDB);
            }
            else
            {
                cacheService = MemoryService.Instance;
               // cacheSessionService = new MemoryService();
            }
            services.AddSingleton<ICacheService>(cacheService);
            services.AddSingleton<IDistributedCache>(cacheService);
           
            services.AddSession(options =>
            {
                options.Cookie.HttpOnly = false;
                //options.Cookie.Domain = ApplicationEnvironments.Site.Cookie.Domain;
                //options.Cookie.Path = ApplicationEnvironments.Site.Cookie.RootPath;
                //options.Cookie.Name = ApplicationEnvironments.Site.Cookie.Name;
                options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
                
                //使会话cookie成为必不可少的
                options.Cookie.IsEssential = true;
                options.IdleTimeout = TimeSpan.FromMinutes(ApplicationEnvironments.Site.SessionTimeout);
            });
            services.AddDirectoryBrowser();
            services.AddAuthentication(options =>{
                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
            {
                //options.Cookie.Domain = ApplicationEnvironments.Site.Cookie.Domain;
                options.DataProtectionProvider= DataProtectionProvider.Create(ApplicationEnvironments.Site.Cookie.ProtectionProviderPath);
                options.Cookie.Name = ApplicationEnvironments.Site.Cookie.Name;
                options.Cookie.Path = ApplicationEnvironments.Site.Cookie.RootPath;
                options.LoginPath = new PathString("/account");
                options.AccessDeniedPath = new PathString("/Forbidden"); //禁止访问路径：当用户试图访问资源时，但未通过该资源的任何授权策略，请求将被重定向到这个相对路径。
                options.SlidingExpiration = true;  //Cookie可以分为永久性的和临时性的。 临时性的是指只在当前浏览器进程里有效，浏览器一旦关闭就失效（被浏览器删除）。 永久性的是指Cookie指定了一个过期时间，在这个时间到达之前，此cookie一直有效（浏览器一直记录着此cookie的存在）。 slidingExpriation的作用是，指示浏览器把cookie作为永久性cookie存储，但是会自动更改过期时间，以使用户不会在登录后并一直活动，但是一段时间后却自动注销。也就是说，你10点登录了，服务器端设置的TimeOut为30分钟，如果slidingExpriation为false,那么10:30以后，你就必须重新登录。如果为true的话，你10:16分时打开了一个新页面，服务器就会通知浏览器，把过期时间修改为10:46。 更详细的说明还是参考MSDN的文档。
            });
            #endregion
            ApplicationEnvironments.DefaultSession = new BaseController();
            //数据库驱动注入
            if (ApplicationEnvironments.Site.IsUseEF)
            {
                services.AddScoped<IDbRepository, EFRepository>();
            }
            else
            {
                services.AddScoped<IDbRepository, AdoRepository>();
            }

           var baseDirectory=  AppDomain.CurrentDomain.BaseDirectory;
            if (ApplicationEnvironments.Site.Components.Plug == null)
            {
                ApplicationEnvironments.Site.Components.Plug = new List<SystemComponent>();
            }
            ApplicationEnvironments.Site.Components.Plug.Insert(0, ApplicationEnvironments.Site.Components.System);
            CommonHelper.InitPages();
            #region AutoFac
            #region  mvc 区域分项目时调用
            var manager = new ApplicationPartManager();
            var builder = new Autofac.ContainerBuilder();
            #region Service 层注入
            if (ApplicationEnvironments.Site.Components.Plug != null && ApplicationEnvironments.Site.Components.Plug.Count > 0)
            {
                Regex regex = new Regex("^I([a-zA-Z0-9_\\-]+)Service$");
                AppHttpContext.ServiceInterface = new Dictionary<string, Type>();
                foreach (var components in ApplicationEnvironments.Site.Components.Plug)
                {
                    if (!components.Opened)
                    {
                        continue;
                    }
                    if (components.Service != null && components.Service.Length > 0)
                    {
                        foreach (var item in components.Service)
                        {
                            foreach (var interfaceObj in GetClassInterface(baseDirectory + item))
                            {
                                AppHttpContext.ServiceInterface.Add(regex.Match(interfaceObj.Value.Name).Groups[1].Value, interfaceObj.Value);
                                services.AddScoped(interfaceObj.Value, interfaceObj.Key);
                            }
                        }

                    }
       
                    if (!string.IsNullOrWhiteSpace(components.Mvc))
                    {
                        var controllerObj = new AssemblyPart(AssemblyLoadContext.Default.LoadFromAssemblyPath(baseDirectory + components.Mvc ));
                        if (!string.IsNullOrWhiteSpace(components.Id))
                        {
                            mvcBuilders.ConfigureApplicationPartManager(controller => {
                                controller.ApplicationParts.Add(controllerObj);
                                //if (!string.IsNullOrWhiteSpace(components.Views))
                                //{
                                //    controller.ApplicationParts.Add(new CompiledRazorAssemblyPart(AssemblyLoadContext.Default.LoadFromAssemblyPath(baseDirectory + components.Views)));
                                //}

                            });
                        }
                        
                        manager.ApplicationParts.Add(controllerObj);
                    }
                }

            }
            builder.Populate(services);
            #endregion

            manager.FeatureProviders.Add(new ControllerFeatureProvider());
            var feature = new ControllerFeature();
            manager.PopulateFeature(feature);
            #endregion

            //采用属性注入控制器
            builder.RegisterTypes(feature.Controllers.Select(ti => ti.AsType()).ToArray()).PropertiesAutowired();
            this.ApplicationContainer = builder.Build();
            return new AutofacServiceProvider(this.ApplicationContainer);
           
            #endregion
            //跨域访问

            //services.AddCors(options => options.AddPolicy("AllowSameDomain", builder => builder.WithOrigins("http://a.local.com:50307", "http://b.local.com:63455")));
            //跨域认证时使用此项
            //services.AddCors(options => options.AddPolicy("AllowSameDomain", builder => builder.WithOrigins("http://a.local.com:50307", "http://b.local.com:63455").AllowCredentials()));
        }
        

        #region 获取需要注入的mvc service dll
    

        /// <summary>
        /// 获取服务层实例
        /// </summary>
        /// <param name="assemblyName"></param>
        /// <returns></returns>
        private Dictionary<Type, Type> GetClassInterface(string assemblyName)
        {
            if (!String.IsNullOrEmpty(assemblyName))
            {
                Assembly assembly = Assembly.LoadFrom(assemblyName);
                List<Type> ts = assembly.GetTypes().ToList();
                var result = new Dictionary<Type, Type>();
                foreach (var item in ts.Where(s => !s.IsInterface&&s.IsSubclassOf(typeof(SerivceBase))))
                {
                    var interfaceType = item.GetInterface("I"+item.Name,true);
                    if (item.IsGenericType|| interfaceType==null) continue;
                    result.Add(item, interfaceType);
                }
                return result;
            }
            return new Dictionary<Type, Type>();
        }
        #endregion

        #region Configure
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            ///负载均衡中间件
            app.UseForwardedHeaders();
          
            //请求中间件处理
            app.UseRequestHandling();
            //app.UseExceptionHandler("/Home/Error");//错误处理
            if (env.IsDevelopment())
            {
                //app.UseBrowserLink();
                //app.UseDeveloperExceptionPage();
                ApplicationEnvironments.IsDevelopment = true;
            }
            else
            {
                //app.UseExceptionHandler("/Home/Error");
                //app.UseExceptionHandler("/Home/Error");
                //app.UseHsts();
                ApplicationEnvironments.IsDevelopment = false;
            }

           //app.UseHttpsRedirection(); //强制https跳转
            app.UseStaticFiles(new StaticFileOptions()
            {
                FileProvider = new PhysicalFileProvider(
                Path.Combine(ApplicationEnvironments.DefaultDirectory, @"wwwroot")
                ),
                OnPrepareResponse = ctx =>
                {
                    ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=6000");
                }
            });
            app.UseStaticFiles(new StaticFileOptions()
            {
                FileProvider = new PhysicalFileProvider(
                Path.Combine(ApplicationEnvironments.BaseDirectory, @"upload")
                ),
                RequestPath = new PathString("/static/cloud"),
                OnPrepareResponse = ctx =>
                {
                    ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=6000");
                }

            });
            app.UseDirectoryBrowser(new DirectoryBrowserOptions()
            {
                FileProvider = new PhysicalFileProvider(
                     Path.Combine(ApplicationEnvironments.BaseDirectory, @"upload")),
                RequestPath = new PathString("/static/cloud"),
                
            });
        
            app.UseCookiePolicy();
            app.UseSession();
            app.UseAuthentication();

            app.UseResponseCompression();//文件压缩
            app.UseResponseCaching();
            app.Map("/socket/connect", WebSocketHelper.Map);
         
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                name: "areaRoute",
                template: "{area:exists}/{controller=Home}/{action=Index}/{id?}");//控制器分层
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}"
                    );
            });

            //添加httpcontext类
            AppHttpContext.Configure(app.ApplicationServices.
     GetRequiredService<Microsoft.AspNetCore.Http.IHttpContextAccessor>());
            ApplicationEnvironments.DefaultSession = new BaseController();
            AppHttpContext.CacheService = app.ApplicationServices.GetService<ICacheService>();
            using (EFRepository eFRepository = new EFRepository())
            {
                try
                {
                    if (eFRepository.DbEnsureCreated())
                    {
                        //InitDataService initDataService = new InitDataService();
                        //initDataService.InsertData(eFRepository);
                        //initDataService.DeleteInitConfig();
                    }
                }
                catch (Exception ex)
                {
                    // LogHelper.Info(ex.Message, this);
                }
            }
            // ApplicationEnvironments.DefaultSession= 
            //跨域访问
            // app.UseCors("AllowSameDomain");
            //app.UseSpa(spa =>
            //{
            //    // To learn more about options for serving an Angular SPA from ASP.NET Core,
            //    // see https://go.microsoft.com/fwlink/?linkid=864501

            //    spa.Options.SourcePath = "ClientApp";

            //    if (env.IsDevelopment())
            //    {
            //        spa.UseAngularCliServer(npmScript: "start");
            //    }
            //});
            //nginx反向代理
            app.UseForwardedHeaders(new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            });
        }
        #endregion

    }

}
